package com.basker.pisces.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

public abstract class AbstractMapList<TKey, TItem> extends ArrayList<TItem> {

    private static final long serialVersionUID = 2508412330221177754L;

    private Map<TKey, TItem> map = new HashMap<>();

    @Override
    public void add(int index, TItem element) {
        if (this.addToMap(element)) {
            super.add(index, element);
        }
    }

    @Override
    public boolean add(TItem e) {
        if (this.addToMap(e)) {
            return super.add(e);
        }

        return false;
    }

    @Override
    public boolean addAll(Collection<? extends TItem> c) {
        List<TItem> nc = new ArrayList<>(c.size());

        for (TItem item : c) {
            if (this.addToMap(item)) {
                nc.add(item);
            }
        }

        if (nc.isEmpty()) {
            return false;
        }

        return super.addAll(c);
    }

    @Override
    public boolean addAll(int index, Collection<? extends TItem> c) {
        List<TItem> nc = new ArrayList<>(c.size());

        for (TItem item : c) {
            if (this.addToMap(item)) {
                nc.add(item);
            }
        }

        if (nc.isEmpty()) {
            return false;
        }

        return super.addAll(index, c);
    }

    @Override
    public void clear() {
        super.clear();
        this.map.clear();
    }

    public TItem get(TKey key) {
        return map.get(key);
    }

    @Override
    public TItem remove(int index) {
        TItem remove = super.remove(index);
        if (remove != null) {
            this.map.remove(this.getKey(remove));
        }

        return remove;
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean remove(Object o) {
        if (super.remove(o)) {
            this.map.remove(this.getKey((TItem) o));
            return true;
        }

        return false;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        boolean removeAny = false;

        for (Object o : c) {
            if (this.remove(o)) {
                removeAny = true;
            }
        }

        return removeAny;
    }

    @Override
    public boolean removeIf(Predicate<? super TItem> filter) {
        List<TItem> nc = new ArrayList<>(this.size());

        for (TItem item : this) {
            if (filter.test(item)) {
                nc.add(item);
            }
        }

        if (nc.isEmpty()) {
            return false;
        }

        return this.removeAll(nc);
    }

    protected abstract TKey getKey(TItem item);

    private boolean addToMap(TItem element) {
        TKey key = this.getKey(element);
        if (this.map.containsKey(key)) {
            return false;
        }

        this.map.put(key, element);

        return true;
    }
}
